import * as THREE from '../../build/three.module.js';
import Stats from '../../jsm/libs/stats.module.js';
import { GUI } from '../../jsm/libs/dat.gui.module.js';

import { FirstPersonControls } from '../../jsm/controls/FirstPersonControls.js';
import { ImprovedNoise } from '../../jsm/math/ImprovedNoise.js';

const width = 256,
      depth = 256,
      halfWidth = width / 2,
      halfDepth = depth / 2;

const params = {
  fog: true
}

/**
 * 核心代码，柏林噪声算法计算地形高度值
 */
const generateHeight = (width, depth) => {
  const size = width * depth,
        data = new Uint8Array(size),
        perlin = new ImprovedNoise(),
        z = Math.random() * 100;
  let quality = 1;

  for(let j = 0; j < 4; j += 1) {
    for(let i = 0; i < size; i += 1) {
      const x = i % width, y = ~~(i / width);
      data[i] += Math.abs( perlin.noise(x / quality, y / quality, z) * quality * 1.75 )
    }

    quality *= 5;
  }

  return data;
}

const generateTexture = (data, width, height) => {
  const canvas = document.createElement('canvas'),
        vector3 = new THREE.Vector3(0, 0, 0),
        sun = new THREE.Vector3(1, 1, 1);
  
  let ctx = canvas.getContext('2d');

  sun.normalize(); // 单位化
  canvas.width = width;
  canvas.height = height;

  ctx.fillStyle = '#000';
  ctx.fillRect(0, 0, width, height);

  let image = ctx.getImageData(0, 0, canvas.width, canvas.height);
  let imageData = image.data;

  /**
   * 此段代码是处理明暗阴影，sun 相当于一个平行光
   */
  for(let i = 0, j = 0, l = imageData.length; i < l; i += 4, j += 1) {
    vector3.x = data[j - 2] - data[j + 2];
    vector3.y = 2;
    vector3.z = data[ j - width * 2 ] - data[ j + width * 2 ];
    vector3.normalize();

    const shade = vector3.dot(sun);
    
    imageData[i] = (96 + shade * 128) * (.5 + data[j] * .007);
    imageData[i + 1] = (32 + shade * 96) * (.5 + data[j] * .007);
    imageData[i + 2] = (shade * 96) * (.5 + data[j] * .007);
  }

  ctx.putImageData(image, 0, 0);

  // 放大4倍
  const canvasScaled = document.createElement('canvas');
  canvasScaled.width = width * 4;
  canvasScaled.height = height * 4;

  ctx = canvasScaled.getContext('2d');
  ctx.scale(4, 4);
  ctx.drawImage(canvas, 0, 0);

  image = ctx.getImageData(0, 0, canvasScaled.width, canvasScaled.height);
  imageData = image.data;

  /**
   * 此段代码为像素给入底色
   */
  for(let i = 0, l = imageData.length; i < l; i += 4) {
    const v = ~~(Math.random() * 5);

    imageData[i] += v;
    imageData[i + 1] += v;
    imageData[i + 2] += v;
  }

  ctx.putImageData(image, 0, 0);

  return canvasScaled;
}

const data = generateHeight(width, depth);
console.log(data);

const clock = new THREE.Clock();
const rayCaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

const scene = new THREE.Scene();
scene.background = new THREE.Color(0xbfd1e5);

const camera = new THREE.PerspectiveCamera(65, innerWidth / innerHeight, 1, 10000);
camera.position.y = data[halfWidth + halfDepth * width] * 10 + 500;

const gmt = new THREE.PlaneBufferGeometry(7500, 7500, width - 1, depth - 1);
gmt.rotateX(- Math.PI / 2);

const vertices = gmt.attributes.position.array;
for(let i = 0, j = 0, l = vertices.length; i < l; i += 1, j += 3) {
  const newV = data[i] * 10;
  vertices[j + 1] = newV;
}
// console.log(vertices);

const texture = new THREE.CanvasTexture( generateTexture(data, width, depth) );
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;

const mesh = new THREE.Mesh(gmt, new THREE.MeshBasicMaterial({ map: texture }));
scene.add(mesh);

const gmtCone = new THREE.ConeBufferGeometry(20, 100, 3);
gmtCone.translate(0, 50, 0);
gmtCone.rotateX(Math.PI / 2);
const helper = new THREE.Mesh(gmtCone, new THREE.MeshNormalMaterial());
scene.add(helper);

const points = new THREE.Points(gmt, new THREE.PointsMaterial({ color: 0xff0000, size: 4 }))
scene.add(points);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(devicePixelRatio);
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

const controls = new FirstPersonControls(camera, renderer.domElement);
controls.movementSpeed = 150;
controls.lookSpeed = .1;

const stats = new Stats();
document.body.appendChild(stats.dom);

const FogChange = () => {
  if (params.fog) {
    scene.fog = new THREE.FogExp2(0xefd1b5, .0025);
  } else {
    scene.fog = null
  }
}
FogChange();

const onMouseMove = (e) => {
  mouse.x = (e.clientX / renderer.domElement.clientWidth) * 2 - 1;
  mouse.y = - (e.clientY / renderer.domElement.clientHeight) * 2 + 1;
  rayCaster.setFromCamera(mouse, camera);

  const intersects = rayCaster.intersectObject(mesh);
  
  if (intersects.length) {
    helper.position.set(0, 0, 0);
    helper.lookAt(intersects[0].face.normal);
  
    helper.position.copy(intersects[0].point);
  }
}
document.addEventListener('mousemove', onMouseMove, false);

const gui = new GUI();
gui.addFolder('控制');
gui.add(params, 'fog').name('迷雾').onChange(FogChange);

const render = () => {
  controls.update(clock.getDelta());
  renderer.render(scene, camera);
}

const animte = () => {
  requestAnimationFrame(animte);

  render();
  stats.update();
}
animte();

const handleWindowResize = () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(innerWidth, innerHeight);

  controls.handleResize();
}
window.addEventListener('resize', handleWindowResize, false);